package com.uiotsoft.small.mqtt;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hivemq.client.mqtt.MqttClientSslConfig;
import com.hivemq.client.mqtt.MqttClientSslConfigBuilder;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import com.hivemq.client.mqtt.mqtt3.Mqtt3BlockingClient;
import com.hivemq.client.mqtt.mqtt3.Mqtt3Client;
import com.hivemq.client.mqtt.mqtt3.Mqtt3ClientBuilder;
import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth;
import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuthBuilder;
import com.hivemq.client.mqtt.mqtt3.message.connect.Mqtt3ConnectBuilder;
import com.hivemq.client.mqtt.mqtt3.message.connect.connack.Mqtt3ConnAck;
import com.hivemq.client.mqtt.mqtt3.message.publish.Mqtt3Publish;
import com.uiotsoft.entity.MessageConvert;
import com.uiotsoft.entity.MessageWrapper;
import com.uiotsoft.small.config.APPConfig;
import com.uiotsoft.util.encrypt.small.SignUtil;
import com.uiotsoft.util.mqtt.MqttMessageUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;


import javax.net.ssl.TrustManagerFactory;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;

@Data
@Slf4j
public class MqttClientHive {
    private String host;
    private String clientId;
    private String username;
    private String password;
    private String appKey;
    private String appSecret;
    private String willTopic;
    private byte[] willData;
    private volatile String result;
    private volatile String topic;
    private final Mqtt3BlockingClient client;

    public MqttClientHive(String host, String clientId, String username, String password) {
        this.host = host;
        this.clientId = clientId;
        this.username = username;
        this.password = password;
        Mqtt3ClientBuilder mqtt3ClientBuilder = Mqtt3Client.builder();
        if (host.startsWith("ssl")){
            host = host.substring(6);
            String[] split = host.split(":");
            mqtt3ClientBuilder.sslConfig(getSslConfig());
            mqtt3ClientBuilder.identifier(clientId)
                    .serverHost(split[0])
                    .serverPort(Integer.parseInt(split[1]));
        }
        client = mqtt3ClientBuilder.buildBlocking();
    }

    private Mqtt3SimpleAuth createAuth() {
        if (username != null) {
            Mqtt3SimpleAuthBuilder.Complete simpleAuth = Mqtt3SimpleAuth.builder()
                    .username(username);
            if (password != null) {
                simpleAuth.password(password.getBytes());
            }
            return simpleAuth.build();
        }
        return null;
    }

    public MqttClientSslConfig getSslConfig() {
        MqttClientSslConfigBuilder sslBuilder = MqttClientSslConfig.builder()
                .protocols(Collections.singletonList("TLSv1.2"));
        TrustManagerFactory acceptAllTmFactory = AcceptAllTrustManagerFactory.getInstance();
        sslBuilder = sslBuilder.trustManagerFactory(acceptAllTmFactory);
        MqttClientSslConfig sslConfig = sslBuilder.build();
        return sslConfig;
    }

    public Mqtt3BlockingClient connect() {
        Mqtt3ConnectBuilder.Send<CompletableFuture<Mqtt3ConnAck>> completableFutureSend = client.toAsync().connectWith()
                .cleanSession(false)
                .keepAlive(30)
                .simpleAuth(createAuth());
        if (willTopic != null && !willTopic.equals("")) {
            completableFutureSend.willPublish()
                    .topic(willTopic)
                    .qos(MqttQos.AT_MOST_ONCE)
                    .payload(willData)
                    .applyWillPublish();
        }
        try {
            completableFutureSend.send().get(10, TimeUnit.SECONDS);
        } catch (TimeoutException timeout) {
            try {
                client.disconnect();
            } catch (Exception e) {
                log.debug("Disconnect on timeout failed " + client);
            }
            log.info("Connect timeout client: " + clientId);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        subscribe(topic);
        return client;
    }

    public void subscribe(String topic) {
        client.toAsync().subscribeWith()
                .topicFilter(topic)
                .qos(MqttQos.AT_MOST_ONCE)
                .callback(consumer)
                .send();
    }

    public void publish(String topic, String msg) {
        JSONObject msgJson = JSONUtil.parseObj(msg);
        String appkey = msgJson.getJSONObject("header").getStr("appkey");
        log.debug("要发送的数据中appkey是：{}", appkey);
        byte[] send;
        if (appkey.equals("788916ea524bd1b6")) {
            send = SignUtil.getPublishMessage(msg, APPConfig.CHK_appKey,APPConfig.CHK_appSecret);
        } else {
            log.debug("签名的appsecret是：{}", appSecret);
            send = SignUtil.getPublishMessage(msg,appkey, appSecret);
        }
        client.publishWith()
                .topic(topic)
                .payload(send)
                .qos(MqttQos.AT_MOST_ONCE)
                .retain(false)
                .send();

    }

    public void close() {
        client.disconnect();
    }

    Consumer<Mqtt3Publish> consumer = mqtt3Publish -> {
        MessageWrapper messageWrapper = MqttMessageUtil.getMessageData(mqtt3Publish.getPayloadAsBytes());
        String appkey = messageWrapper.getAppkey();
        log.debug("接收到的数据中appkey是：{}", appkey);
        MessageConvert messageConvert;
        if (appkey.equals("788916ea524bd1b6")) {
            messageConvert = MqttMessageUtil.decry(messageWrapper, "5fc1262aa95f914b42cad34469f19a6c");
        } else {
            messageConvert = MqttMessageUtil.decry(messageWrapper, appSecret);
        }
        result = messageConvert.getMessage().toJSONString();
        topic = mqtt3Publish.getTopic().toString();
        log.debug("\n******收到服务返回的消息主题为：{}\n******数据为：{}", topic, result);
    };

}
